/**
 * @ Author     ：LILA3
 * @ Date       ：Created in 4:42 PM 7/13/2018
 */

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;

public abstract class AbstractBaseDao<T> implements BaseDao<T> {
    @Override
    public void save(T entity) {
        String sql = "insert into " + entity.getClass().getSimpleName().toLowerCase() + "(";

        List<Method> list = getMethodWithGet(entity);

        for (Method getMethod : list) {
            String methodName = getMethod.getName();
            sql += methodName.substring(3).toLowerCase() + ",";
        }
        sql = sql.substring(0, sql.lastIndexOf(",")) + ") values(";
        for (Method getMethod : list) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.lastIndexOf(",")) + ") values(";
        System.out.println(sql);

        try {
            int i = 1;
            PreparedStatement statement = ConnectionManager.getInstance().prepareStatement(sql);
            for (Method getMethod : list) {
                if (getMethod.getReturnType().getSimpleName().indexOf("String") != -1) {
                    statement.setString(i++, (String) getMethod.invoke(entity));
                } else if (getMethod.getReturnType().getSimpleName().indexOf("Date") != -1) {
                    statement.setDate(i++, (Date) getMethod.invoke(entity));
                } else {
                    statement.setInt(i++, (Integer) getMethod.invoke(entity));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void update(T entity) {
        String sql = "update" + entity.getClass().getSimpleName().toLowerCase() + "set ";

        List<Method> list = getMethodWithGet(entity);

        for (Method getMethod : list) {
            String methodName = getMethod.getName();
            sql += methodName.substring(3).toLowerCase() + "  = ?,";
        }
        try {
            int id = (int) list.get(0).invoke(entity.getClass());
            sql = sql.substring(0, sql.lastIndexOf(",")) + " where id = " + id;
            list.remove(0);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(sql);

        try {
            int i = 0;
            PreparedStatement statement = ConnectionManager.getInstance().prepareStatement(sql);
            for (Method getMethod : list) {
                if (getMethod.getReturnType().getSimpleName().indexOf("String") != -1) {
                    statement.setString(++i, (String) getMethod.invoke(entity));
                } else if (getMethod.getReturnType().getSimpleName().indexOf("Date") != -1) {
                    statement.setDate(++i, (Date) getMethod.invoke(entity));
                } else {
                    statement.setInt(++i, (Integer) getMethod.invoke(entity));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void delete(T entity) {
        String sql = "delete from " + entity.getClass().getSimpleName().toLowerCase();
        List<Method> list = getMethodWithGet(entity);
        try {
            int id = (int) list.get(0).invoke(entity.getClass());
            sql = sql + " where id = " + id;
            list.remove(0);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        System.out.println(sql);
    }

    @Override
    public T findById(Object obj) throws Exception {
        ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
        Class<T> perClass = (Class<T>) type.getActualTypeArguments()[0];
        String sql = "select * from " + perClass.getSimpleName().toLowerCase() + " where id = " + obj.toString();
        PreparedStatement statement = ConnectionManager.getInstance().prepareStatement(sql);

        T entity = perClass.newInstance();

        List<Method> list = getMethodWithSet(entity);
        ResultSet rs = statement.executeQuery();
        int i = 0;
        for (Method method : list) {
            rs.next();
            if (method.getParameterTypes()[0].getSimpleName().indexOf("String") != -1) {
                method.invoke(entity.getClass(), rs.getString(method.getName().substring(3).toLowerCase()));
            } else if (method.getParameterTypes()[0].getSimpleName().indexOf("Date") != -1) {
                method.invoke(entity.getClass(), rs.getDate(method.getName().substring(3).toLowerCase()));
            } else {
                method.invoke(entity.getClass(), rs.getInt(method.getName().substring(3).toLowerCase()));
            }
        }

        rs.close();
        statement.close();
        return entity;
    }


    public List<Method> getMethodWithGet(T entity) {

        List<Method> getMethods = new ArrayList<>();
        Method[] methods = entity.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().startsWith("get")) {
                getMethods.add(method);
            }
        }

        return getMethods;
    }

    public List<Method> getMethodWithSet(T entity) {

        List<Method> getMethods = new ArrayList<>();
        Method[] methods = entity.getClass().getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().startsWith("set")) {
                getMethods.add(method);
            }
        }

        return getMethods;
    }
}

